package com.by.toolkitspring.customizeannotation;

import cn.hutool.core.lang.ClassScanner;
import cn.hutool.core.util.ClassUtil;
import cn.hutool.core.util.ReflectUtil;
import com.google.common.collect.Maps;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;

import java.lang.annotation.Annotation;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 扫描所有自定义的类注解信息
 *
 * @author YanBingHao
 * @since 2023/12/25
 */
@Slf4j
@Configuration
@SuppressWarnings({"unused", "rawtypes"})
public class ByAnnotationProcessor implements BeanFactoryPostProcessor, Ordered {

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
        log.info("init customizeAnnotationProcessor start");

        List<String> basePackageList = ByScannerRegistrar.getBasePackageList();
        List<Class<? extends Annotation>> annotationClassList = ByScannerRegistrar.getAnnotationClassList();

        if (CollectionUtils.isEmpty(annotationClassList)) {
            log.info("init customizeAnnotationProcessor annotation class is null");
            return;
        }

        Map<Class<?>, Set<Class<?>>> annotationClazzMap = Maps.newHashMap();

        Map<? extends Class<? extends Annotation>, List<CustomizeAnnotationInfo>> customizeAnnotationInfoMap = annotationClassList.stream()
                .map(annotationClazz -> {
                    Set<Class<?>> classes = basePackageList.stream()
                            .map(packageName -> ClassScanner.scanAllPackageByAnnotation(packageName, annotationClazz))
                            .filter(Objects::nonNull)
                            .flatMap(Collection::stream)
                            .collect(Collectors.toSet());

                    if (CollectionUtils.isEmpty(classes)) {
                        return null;
                    }

                    return classes.stream()
                            .map(clazz -> {
                                Annotation annotation = clazz.getAnnotation(annotationClazz);
                                if (Objects.isNull(annotation)) {
                                    return null;
                                }
                                return CustomizeAnnotationInfo.builder()
                                        .annotationClass(annotationClazz)
                                        .annotationInfo(annotation)
                                        .beanClass(clazz)
                                        .build();
                            }).collect(Collectors.toList());
                }).filter(CollectionUtils::isNotEmpty)
                .flatMap(Collection::stream)
                .collect(Collectors.groupingBy(CustomizeAnnotationInfo::getAnnotationClass, Collectors.toList()));

        log.info("customizeAnnotationProcessor customizeAnnotationInfoMap size {}", customizeAnnotationInfoMap.size());

        // 将上下文信息注入到Spring容器，由Spring管理
        configurableListableBeanFactory.registerSingleton(ByAnnotationContext.class.getName(), new ByAnnotationContext(customizeAnnotationInfoMap));

        basePackageList.stream()
                .map(basePackage -> {
                    Set<Class<?>> classes = ClassScanner.scanAllPackageBySuper(basePackage, AbstractByAnnotationContext.class);
                    if (CollectionUtils.isEmpty(classes)) {
                        return null;
                    }
                    return classes;
                }).filter(Objects::nonNull)
                .filter(CollectionUtils::isNotEmpty)
                .flatMap(Collection::stream)
                .distinct()
                .forEach(clazz -> {
                    Class<?> typeArgument = ClassUtil.getTypeArgument(clazz);
                    if (!annotationClassList.contains(typeArgument)) {
                        return;
                    }
                    List<CustomizeAnnotationInfo> infos = customizeAnnotationInfoMap.get(typeArgument);
                    AbstractByAnnotationContext annotationContext = (AbstractByAnnotationContext) ReflectUtil.newInstance(clazz);
                    annotationContext.receiveContext(infos);
                    // 将上下文信息注入到Spring容器，由Spring管理
                    configurableListableBeanFactory.registerSingleton(clazz.getName(), annotationContext);
                });

        log.info("init customizeAnnotationProcessor end");
    }

    @Override
    public int getOrder() {
        return Integer.MIN_VALUE;
    }

    @Data
    @Builder
    @NoArgsConstructor
    @AllArgsConstructor
    public static class CustomizeAnnotationInfo {

        private Class<? extends Annotation> annotationClass;

        private Annotation annotationInfo;

        private Class<?> beanClass;

    }

}
